package com.zlp.mall.modules.ums.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.BCrypt;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zlp.mall.common.api.Pager;
import com.zlp.mall.common.api.RespCode;
import com.zlp.mall.common.enums.StatusEnum;
import com.zlp.mall.common.exception.ApiException;
import com.zlp.mall.common.exception.Asserts;
import com.zlp.mall.common.exception.CustomException;
import com.zlp.mall.common.util.BeanToUtils;
import com.zlp.mall.common.util.MethodUtil;
import com.zlp.mall.modules.ums.dto.req.AddAdminReq;
import com.zlp.mall.modules.ums.dto.UpdateAdminPasswordParam;
import com.zlp.mall.modules.ums.dto.resp.AdminResp;
import com.zlp.mall.modules.ums.mapper.UmsAdminLoginLogMapper;
import com.zlp.mall.modules.ums.mapper.UmsAdminMapper;
import com.zlp.mall.modules.ums.mapper.UmsResourceMapper;
import com.zlp.mall.modules.ums.mapper.UmsRoleMapper;
import com.zlp.mall.modules.ums.model.*;
import com.zlp.mall.modules.ums.service.UmsAdminCacheService;
import com.zlp.mall.modules.ums.service.UmsAdminRoleRelationService;
import com.zlp.mall.modules.ums.service.AdminService;
import com.zlp.mall.security.dto.AdminUserDetails;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * 后台管理员管理Service实现类
 * Created by macro on 2018/4/26.
 */
@Service
@Slf4j(topic = "AdminServiceImpl")
public class AdminServiceImpl extends ServiceImpl<UmsAdminMapper, Admin> implements AdminService {


    @Autowired
    private UmsAdminLoginLogMapper loginLogMapper;
    @Autowired
    private UmsAdminCacheService adminCacheService;
    @Autowired
    private UmsAdminRoleRelationService adminRoleRelationService;
    @Autowired
    private UmsRoleMapper roleMapper;
    @Autowired
    private UmsResourceMapper resourceMapper;

    @Override
    public Admin getAdminByUsername(String username) {

        Admin admin;
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Admin::getUsername, username);
        List<Admin> adminList = list(wrapper);
        if (adminList != null && adminList.size() > 0) {
            admin = adminList.get(0);
            adminCacheService.setAdmin(admin);
            return admin;
        }
        return null;
    }

    @Override
    public AdminResp register(AddAdminReq addAdminReq) {

        log.info("register.req addAdminReq={}", JSON.toJSONString(addAdminReq));
        Admin umsAdmin = BeanToUtils.dtoToDo(addAdminReq, Admin.class);
        umsAdmin.setCreateTime(new Date());
        umsAdmin.setStatus(StatusEnum.NORMAL.getValue());
        //查询是否有相同用户名的用户
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Admin::getUsername, umsAdmin.getUsername());
        int count = this.count(wrapper);
        if (count > 0) {
            throw new CustomException(RespCode.BACK_SYS_2000, MethodUtil.getLineInfo());
        }
        //将密码进行加密操作
        String encodePassword;
        try {
            encodePassword = BCrypt.hashpw(umsAdmin.getPassword());
        } catch (Exception e) {
            log.error("BCrypt.hashpw.resp error={}", e.getMessage());
            throw new CustomException(RespCode.BACK_SYS_2002, MethodUtil.getLineInfo());
        }
        umsAdmin.setPassword(encodePassword);
        this.save(umsAdmin);
        if (Objects.isNull(umsAdmin.getId())) {
            throw new CustomException(RespCode.BACK_SYS_2001, MethodUtil.getLineInfo());
        }
        return BeanToUtils.doToDto(umsAdmin, AdminResp.class);
    }

    @Override
    public Admin login(String username, String password) {

        //密码需要客户端加密后传递
        Admin umsAdmin = null;
        try {
            umsAdmin = loadUserByUsername(username);
            AdminUserDetails userDetails = loadAdminByUsername(username);
            umsAdmin = userDetails.getUmsAdmin();
            if (!BCrypt.checkpw(password, umsAdmin.getPassword())) {
                Asserts.fail("密码不正确");
            }
            if (!userDetails.isEnabled()) {
                Asserts.fail("帐号已被禁用");
            }
            // 生成 SpringSecurity 的通过认证标识
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            insertLoginLog(username);
        } catch (Exception e) {
            Asserts.fail("登录异常:" + e.getMessage());
        }
        return umsAdmin;
    }

    /**
     * 添加登录记录
     *
     * @param username 用户名
     */
    private void insertLoginLog(String username) {
        Admin admin = getAdminByUsername(username);
        if (admin == null) return;
        UmsAdminLoginLog loginLog = new UmsAdminLoginLog();
        loginLog.setAdminId(admin.getId());
        loginLog.setCreateTime(new Date());
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        loginLog.setIp(request.getRemoteAddr());
        loginLogMapper.insert(loginLog);
    }

    /**
     * 根据用户名修改登录时间
     */
    private void updateLoginTimeByUsername(String username) {
        Admin record = new Admin();
        record.setLoginTime(new Date());
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Admin::getUsername, username);
        update(record, wrapper);
    }


    @Override
    public Pager<AdminResp> list(String keyword, Integer pageSize, Integer pageNum) {

        Page<Admin> page = new Page<>(pageNum, pageSize);
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        LambdaQueryWrapper<Admin> lambda = wrapper.lambda();
        if (StrUtil.isNotEmpty(keyword)) {
            lambda.like(Admin::getUsername, keyword);
            lambda.or().like(Admin::getNickName, keyword);
        }
        Page<Admin> adminPage = page(page, wrapper);
        if (CollectionUtil.isNotEmpty(adminPage.getRecords())) {
            List<AdminResp> adminResps = BeanToUtils.entityToList(page.getRecords(), AdminResp.class);
            return new Pager(adminResps, page.getTotal(), pageSize, pageNum);
        }
        return new Pager<>();
    }

    @Override
    public boolean update(Long id, Admin admin) {
        admin.setId(id);
        Admin rawAdmin = getById(id);
        if (rawAdmin.getPassword().equals(admin.getPassword())) {
            //与原加密密码相同的不需要修改
            admin.setPassword(null);
        } else {
            //与原加密密码不同的需要加密修改
            if (StrUtil.isEmpty(admin.getPassword())) {
                admin.setPassword(null);
            } else {
                admin.setPassword(BCrypt.hashpw(admin.getPassword()));
            }
        }
        boolean success = updateById(admin);
        adminCacheService.delAdmin(id);
        return success;
    }

    @Override
    public boolean delete(Long id) {
        adminCacheService.delAdmin(id);
        boolean success = removeById(id);
        adminCacheService.delResourceList(id);
        return success;
    }

    @Override
    public int updateRole(Long adminId, List<Long> roleIds) {
        int count = roleIds == null ? 0 : roleIds.size();
        //先删除原来的关系
        QueryWrapper<UmsAdminRoleRelation> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(UmsAdminRoleRelation::getAdminId, adminId);
        adminRoleRelationService.remove(wrapper);
        //建立新关系
        if (!CollectionUtils.isEmpty(roleIds)) {
            List<UmsAdminRoleRelation> list = new ArrayList<>();
            for (Long roleId : roleIds) {
                UmsAdminRoleRelation roleRelation = new UmsAdminRoleRelation();
                roleRelation.setAdminId(adminId);
                roleRelation.setRoleId(roleId);
                list.add(roleRelation);
            }
            adminRoleRelationService.saveBatch(list);
        }
        adminCacheService.delResourceList(adminId);
        return count;
    }

    @Override
    public List<UmsRole> getRoleList(Long adminId) {
        return roleMapper.getRoleList(adminId);
    }

    @Override
    public List<UmsResource> getResourceList(Long adminId) {

        List<UmsResource> resourceList = adminCacheService.getResourceList(adminId);
        if (CollUtil.isNotEmpty(resourceList)) {
            return resourceList;
        }
        resourceList = resourceMapper.getResourceList(adminId);
        if (CollUtil.isNotEmpty(resourceList)) {
            adminCacheService.setResourceList(adminId, resourceList);
        }
        return resourceList;
    }

    @Override
    public int updatePassword(UpdateAdminPasswordParam param) {
        if (StrUtil.isEmpty(param.getUsername())
                || StrUtil.isEmpty(param.getOldPassword())
                || StrUtil.isEmpty(param.getNewPassword())) {
            return -1;
        }
        QueryWrapper<Admin> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(Admin::getUsername, param.getUsername());
        List<Admin> adminList = list(wrapper);
        if (CollUtil.isEmpty(adminList)) {
            return -2;
        }
        Admin umsAdmin = adminList.get(0);
        if (!BCrypt.checkpw(param.getOldPassword(), umsAdmin.getPassword())) {
            return -3;
        }
        umsAdmin.setPassword(BCrypt.hashpw(param.getNewPassword()));
        updateById(umsAdmin);
        adminCacheService.delAdmin(umsAdmin.getId());
        return 1;
    }

    @Override
    public Admin loadUserByUsername(String username) {
        //获取用户信息
        Admin admin = getAdminByUsername(username);
        if (admin != null) {
            return admin;
        }
        throw new ApiException("用户不存在");
    }

    @Override
    public AdminUserDetails loadAdminByUsername(String username) {

        Admin admin = loadUserByUsername(username);
        // 查询用户相对应的角色
        List<UmsRole> roleList = getRoleList(admin.getId());
        AdminUserDetails adminUserDetails = new AdminUserDetails(admin, roleList);
        return adminUserDetails;

    }
}
